package cn.xinfei.xdecision.engine.runner.executor;

import cn.xinfei.xdecision.common.model.component.strategyout.StrategyOutput;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.vo.FieldInfoVo;
import cn.xinfei.xdecision.constant.GlobalConstant;
import cn.xinfei.xdecision.engine.runner.context.PipelineContext;
import cn.xinfei.xdecision.engine.runner.context.PipelineContextHolder;
import cn.xinfei.xdecision.engine.runner.context.PipelineMetaInfoContext;
import cn.xinfei.xdecision.engine.runner.context.XDecisionContext;
import cn.xinfei.xdecision.engine.runner.executor.model.StateTransmitModel;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
public class DecisionFlow {
    public static NodeHandler getFirst() {
        XDecisionContext context = XDecisionContext.getContext();
        PipelineMetaInfoContext pipelineMetaInfoContext = context.getPipelineMetaInfoContext();
        PipelineContext pipelineContext = context.getPipelineContext();
        String engineCode = pipelineContext.getEngineCode();
        AtomicReference<NodeHandler> nodeHandler = new AtomicReference<>(pipelineMetaInfoContext.get("ND_START"));
        if (null == nodeHandler.get()) {
            Optional<Map.Entry<String, NodeHandler>> first = pipelineMetaInfoContext.entrySet().stream().filter(map -> {
                NodeHandler handler = map.getValue();
                boolean b = handler.getNodeType().equals(NodeTypeEnum.START.getType()) && handler.getEngineCode().equals(engineCode);
                return b;
            }).findFirst();
            first.ifPresent(opt -> {
                nodeHandler.set(opt.getValue());
            });
        }
        return nodeHandler.get();
    }


    public static Set<String> preload() {
        Set<String> fieldCodes = new HashSet<>();
        XDecisionContext context = XDecisionContext.getContext();
        Map<String, Object> contextValue = PipelineContextHolder.getContextValue();
        PipelineMetaInfoContext pipelineMetaInfoContext = context.getPipelineMetaInfoContext();
        if (pipelineMetaInfoContext != null) {
            Set<Map.Entry<String, NodeHandler>> nodeList = pipelineMetaInfoContext.entrySet();
            Iterator<Map.Entry<String, NodeHandler>> iterator = nodeList.iterator();
            //从输出输入变量中，提取出需要预加载的变量
            while (iterator.hasNext()) {
                Map.Entry<String, NodeHandler> node = iterator.next();
                NodeHandler nodeHandler = node.getValue();
                Map<String, FieldInfoVo> inputFields = nodeHandler.getInputFields();
                if (inputFields != null && inputFields.size() > 0) {
                    Collection<FieldInfoVo> values = inputFields.values();
                    Iterator<FieldInfoVo> iterator1 = values.iterator();
                    while (iterator1.hasNext()) {
                        FieldInfoVo fieldVo = iterator1.next();
                        String fieldCode = fieldVo.getFieldCode();
                        if (contextValue.containsKey(fieldCode) == false) {
                            fieldCodes.add(fieldCode);
                        }
                    }
                }
                Map<String, StrategyOutput> outputFields = nodeHandler.getOutputFields();
                if (outputFields != null && outputFields.size() > 0) {
                    Collection<StrategyOutput> values = outputFields.values();
                    Iterator<StrategyOutput> iterator1 = values.iterator();
                    while (iterator1.hasNext()) {
                        StrategyOutput fieldVo = iterator1.next();
                        String fieldCode = fieldVo.getFieldCode();
                        if (contextValue.containsKey(fieldCode) == false) {
                            fieldCodes.add(fieldCode);
                        }
                    }
                }
            }
        }
        return fieldCodes;
    }


    /**
     * 此方法不对外暴露
     *
     * @param curr
     * @return
     */
    protected static StateTransmitModel run(NodeHandler curr) {
        StateTransmitModel transmitModel = null;
        if(curr!=null){
            curr.initContext();
        }
        while (null != curr) {
            log.info("node start");
            //0.节点入栈
            curr.push();
            EngineNode engineNode = curr.getEngineNode();
            String nodeCode = engineNode.getNodeCode();
            MDC.put(GlobalConstant.NID, nodeCode);
            long start = System.currentTimeMillis();
            transmitModel = curr.handler();
            //9.节点出栈
            curr.pop(transmitModel);
            long end = System.currentTimeMillis();
            log.info("node end,EngineResultEnum={},NextNode={},originNextNode={},duration={}", transmitModel.getEngineResultEnum(), transmitModel.getNextNodeCode(), engineNode.getNextNodeCodes(), end - start);
            boolean isEnd = curr.end(transmitModel);
            if (isEnd) {
                break;
            }
            curr = curr.next(transmitModel);
        }
        log.debug("pipeline end");
        return transmitModel;
    }

}
